C# Socket使用以及DotNetty和Supersocket 框架 您所在的位置:网站首页 socket netty区别 C# Socket使用以及DotNetty和Supersocket 框架

C# Socket使用以及DotNetty和Supersocket 框架

2024-07-10 20:53| 来源: 网络整理| 查看: 265

1.Socket服务端与客户端通话

1服务端

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace tSocket { class Program { byte[] bytes = new byte[1024]; Socket cSocket; static void Main(string[] args) { Program p = new Program(); //打开链接 p.open(); //向服务端发送消息 Console.WriteLine("请输入你要对服务端发送的消息:"); string mes = Console.ReadLine(); string con = p.messge(mes); Console.WriteLine("接受到服务端的消息:" + con); } byte[] data = new byte[1024]; string messge(string mes) { //将发送的消息转成字节数组 bytes = Encoding.UTF8.GetBytes(mes); //发送 cSocket.Send(bytes); while (true) { //接受服务端发送的消息,放入字节数组 int len = cSocket.Receive(data); //将字节数组转成可读明文 string con = Encoding.UTF8.GetString(data, 0, len); ////返回 return con; } } /// /// 打开链接 /// void open() { //创建Socket对象 指定连接方式 cSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //创建IP,端口 IPAddress ip = IPAddress.Parse("10.116.253.10"); int port = 7526; //封装IP和端口 IPEndPoint Ipoint = new IPEndPoint(ip, port); //打开链接 cSocket.Connect(Ipoint); } } }

2.客户端

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace ServerSocket { class Program { static void Main(string[] args) { //创建Socket对象,指定他的链接方式 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //建立IP string ip = "10.116.253.10"; //创建端口 int prot = 7526;//1~9999 IPAddress IPAdd = IPAddress.Parse(ip); //封装IP和端口 IPEndPoint point = new IPEndPoint(IPAdd, prot); //绑定IP和端口 serverSocket.Bind(point); //开始监听 serverSocket.Listen(100); Console.WriteLine("开始监听!"); int i = 0; while (true) { i++; //接受客户链接 Socket cSocket = serverSocket.Accept(); Console.WriteLine("接受第"+i+"个客户的连接!"); Client c = new Client(cSocket); } } } } using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ServerSocket { class Client { Socket sSocket; byte[] data = new byte[1024]; Thread t; public Client(Socket cSocket) { //接受客户的连接 sSocket = cSocket; //创建线程 t = new Thread(Mess); //开始线程 t.Start(); } void Mess() { try { while (true) { //将用户发送的数据以一个字节数组装起 int length = sSocket.Receive(data); Console.WriteLine("接受客户端发的消息!"); string mess = Encoding.UTF8.GetString(data, 0, length); if (mess == "con") { string con = "DataSource =."; byte[] bytes = Encoding.UTF8.GetBytes(con); sSocket.Send(bytes); } Console.WriteLine("接到用户的消息:" + mess); } } catch (Exception) { sSocket.Close(); } } } } 2.DotNetty

DotNetty是微软的Azure团队,使用C#实现的Netty的版本发布。不但使用了C#和.Net平台的技术特点,并且保留了Netty原来绝大部分的编程接口。让我们在使用时,完全可以依照Netty官方的教程来学习和使用DotNetty应用程序。

Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

优点 关注点分离——业务和网络逻辑解耦; 模块化和可复用性; 可测试性作为首要的要求 历史 阻塞Socket通信特点: 建立连接要阻塞线程,读取数据要阻塞线程 如果要管理多个客户端,就需要为每个客户端建立不同的线程 会有大量的线程在休眠状态,等待接收数据,资源浪费 每个线程都要占用系统资源 线程的切换很耗费系统资源 非阻塞Socket(NIO)特点: 如图,每个Socket如果需要读写操作,都通过事件通知的方式通知选择器,这样就实现了一个线程管理多个Socket的目的。 选择器甚至可以在所有的Socket空闲的时候允许线程先去干别的事情 减少了线程数量导致的资源占用,减少了线程切换导致的资源消耗 Netty设计的关键点

异步和事件驱动是Netty设计的关键

核心组件 Channel:一个连接就是一个Channel 回调:通知的基础

官方也提供了一些例子。地址如下

https://github.com/Azure/DotNetty

3.Supersocket 

开源地址https://github.com/kerryjiang/SuperSocket

SuperSocket是重量轻的可扩展套接字应用程序框架。您可以使用它轻松构建始终连接的套接字应用程序,而无需考虑如何使用套接字,如何维护套接字连接以及套接字如何工作。这是一个纯C#项目,旨在进行扩展,因此只要以.NET语言开发它们,就可以轻松地将它们集成到您的现有系统中。

首先安装:SuperSocket.Engine

SuperSoket的三大对象:

Session: 每一个用户连接就是一个Session AppServer: Socket服务器实例 Commands: 客户端向服务器发送消息的命令集合

首先在配置文件加入如下配置

AppServer代码如下

[AuthorisizeFilter] public class ChatServer:AppServer { protected override bool Setup(IRootConfig rootConfig, IServerConfig config) { Console.WriteLine("准备读取配置文件。。。。"); return base.Setup(rootConfig, config); } protected override void OnStarted() { Console.WriteLine("Chat服务启动。。。"); base.OnStarted(); } protected override void OnStopped() { Console.WriteLine("Chat服务停止。。。"); base.OnStopped(); } /// /// 新的连接 /// /// protected override void OnNewSessionConnected(ChatSession session) { Console.WriteLine($"Chat服务新加入的连接:{session.LocalEndPoint.Address.ToString()}"); base.OnNewSessionConnected(session); } }

Session代码如下

/// /// 表示用户连接 /// //[AuthorisizeFilter] public class ChatSession : AppSession { public string Id { get; set; } public string PassWord { get; set; } public bool IsLogin { get; set; } public DateTime LoginTime { get; set; } public DateTime LastHbTime { get; set; } public bool IsOnline { get { return this.LastHbTime.AddSeconds(10) > DateTime.Now; } } /// /// 消息发送 /// /// public override void Send(string message) { Console.WriteLine($"准备发送给{this.Id}:{message}"); base.Send(message.Format()); } protected override void OnSessionStarted() { this.Send("Welcome to SuperSocket Chat Server"); } protected override void OnInit() { this.Charset = Encoding.GetEncoding("gb2312"); base.OnInit(); } protected override void HandleUnknownRequest(StringRequestInfo requestInfo) { Console.WriteLine("收到命令:" + requestInfo.Key.ToString()); this.Send("不知道如何处理 " + requestInfo.Key.ToString() + " 命令"); } /// /// 异常捕捉 /// /// protected override void HandleException(Exception e) { this.Send($"\n\r异常信息:{ e.Message}"); //base.HandleException(e); } /// /// 连接关闭 /// /// protected override void OnSessionClosed(CloseReason reason) { Console.WriteLine("链接已关闭。。。"); base.OnSessionClosed(reason); } }

Commands代码如下 : 客户端发送消息命令 Check 1 123456Check 代表类名 ,1代表session.id(会话ID),1代表session.PassWord (会话密码)

public class Check : CommandBase { public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo) { if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 2) { ChatSession oldSession = session.AppServer.GetAllSessions().FirstOrDefault(a => requestInfo.Parameters[0].Equals(a.Id)); if (oldSession != null) // 说过之前有用户用这个Id 登录过 { oldSession.Send("您的账号已经在他处登录,您已经被踢下线了"); oldSession.Close(); } #region 这里就可以连接数据库进行数据验证做登录 ///--------------------- #endregion session.Id = requestInfo.Parameters[0]; session.PassWord = requestInfo.Parameters[1]; session.IsLogin = true; session.LoginTime = DateTime.Now; session.Send("登录成功"); { // 获取当前登录用户的离线消息 ChatDataManager.SendLogin(session.Id, c => { session.Send($"{c.FromId} 给你发送消息:{c.Message} {c.Id}"); }); } } else { session.Send("参数错误"); } } }

离线消息存储的相关类

public class ChatDataManager { /// /// key是用户id /// List 这个用户的全部消息 /// private static Dictionary Dictionary = new Dictionary(); public static void Add(string userId, ChatModel model) { if (Dictionary.ContainsKey(userId)) { Dictionary[userId].Add(model); } else { Dictionary[userId] = new List() { model }; } } public static void Remove(string userId, string modelId) { if (Dictionary.ContainsKey(userId)) { Dictionary[userId] = Dictionary[userId].Where(m => m.Id != modelId).ToList(); } } public static void SendLogin(string userId, Action action) { if (Dictionary.ContainsKey(userId)) { foreach (var item in Dictionary[userId]) { action.Invoke(item); item.State = 1; } } } } /// /// 一条消息的记录 /// public class ChatModel { /// /// 每条分配个唯一Id /// public string Id { get; set; } /// /// 来源编号 /// public string FromId { get; set; } /// /// 目标编号 /// public string ToId { get; set; } /// /// 消息内容 /// public string Message { get; set; } /// /// 消息时间 /// public DateTime CreateTime { get; set; } /// /// 消息状态 0未发送 1已发送待确认 2确认收到 /// public int State { get; set; } }

基本使用获取离线消息

public class Chat : CommandBase { public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo) { // 还是传递两个参数 1、 要发给谁 ToId 2、消息内容 if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 2) { string toId = requestInfo.Parameters[0]; string message = requestInfo.Parameters[1]; ChatSession toSession = session.AppServer.GetAllSessions().FirstOrDefault(a => toId.Equals(a.Id)); string modelId = Guid.NewGuid().ToString(); if (toSession != null) // 说过之前有用户用这个Id 登录过 { toSession.Send($"{session.Id} 给你发消息:{message} {modelId}"); ChatDataManager.Add(toId, new ChatModel() { FromId = session.Id, ToId = toId, Message = message, Id = modelId, State = 1,// 待确认 CreateTime = DateTime.Now }); } else { ChatDataManager.Add(toId, new ChatModel() { FromId = session.Id, ToId = toId, Message = message, Id = modelId, State = 0,// 未发送 CreateTime = DateTime.Now }); session.Send("消息未发送成功"); } } else { session.Send("参数错误"); } } } public class Confirm : CommandBase { public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo) { if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 1) { string modelId = requestInfo.Parameters[0]; Console.WriteLine($"用户{session.Id} 已确认,收到消息{modelId}"); ChatDataManager.Remove(session.Id, modelId); } else { session.Send("参数错误"); } } }

心跳检测:主要就是定时发送消息,没接到消息就发起重连

public class HB : CommandBase { public override void ExecuteCommand(ChatSession session, StringRequestInfo requestInfo) { if (requestInfo.Parameters != null && requestInfo.Parameters.Length == 1) { if ("R".Equals(requestInfo.Parameters[0])) { session.LastHbTime = DateTime.Now; session.Send("R"); } else { session.Send("参数错误"); } } else { session.Send("参数错误"); } } }

SuperSocket的AOP的使用

class AuthorisizeFilterAttribute : CommandFilterAttribute { public override void OnCommandExecuting(CommandExecutingContext commandContext) { ChatSession session = (ChatSession)commandContext.Session; string command = commandContext.CurrentCommand.Name; if (!session.IsLogin) { if (!command.Equals("Check")) { session.Send($"请先登录,再操作"); commandContext.Cancel = true; } else { } } else if (!session.IsOnline) { session.LastHbTime = DateTime.Now; } } public override void OnCommandExecuted(CommandExecutingContext commandContext) { } }

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有